using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;

namespace edu.stanford.hci.ideas
{
    public enum ZONES
    {
        CLOSEST, CLOSE, FAR, FARTHEST
    }

    public class Ranger
    {
        private SerialInterface serialInterface;

        
        private const int NUMBER_OF_RANGERS = 4;
        private ZONES currentZone = ZONES.FARTHEST;
        private int currentLane = -1;
        private float[] rangerData = new float[NUMBER_OF_RANGERS];
        private List<RangerListener> rangerListeners = new List<RangerListener>();

        public Ranger()
        {
            serialInterface = new SerialInterface(this);
            serialInterface.OpenSerialPort();
        }


        public float getRangerData(int index)
        {
            return rangerData[index];
        }

        public void setRangerData(int index, float value)
        {
            // The middle two rangers on the whiteboard are transposed. This reverses it, so that the left one comes first.

            if (index == 1)
            {
                index = 2;
            }
            else if (index == 2)
            {
                index = 1;
            }
            rangerData[index] = value;
            updateCurrentZone();
            fireDistanceEvent();
        }
        
        private void updateCurrentZone() {
            float maxValue = 0;
            for (int i = 0; i < NUMBER_OF_RANGERS; i++)
            {
                if (maxValue < rangerData[i])
                {
                    maxValue = rangerData[i];
                    currentLane = i;
                }
            }
            //changed from: ZONES newZone = getCurrentZone(maxValue);
            ZONES newZone = getCurrentZone(maxValue, currentZone);
            if (newZone != currentZone)
            {
                currentZone = newZone;
                fireZoneEvent();
            }
        }

        private ZONES getCurrentZone(float value, ZONES lastZone)
        {
            if ((lastZone == ZONES.FAR && value < 0.05f)||
                (lastZone !=ZONES.FAR && value < 0.1f))
            {
                return ZONES.FARTHEST;
            }
            else if ((lastZone == ZONES.CLOSE && value < 0.15f)||
                (lastZone !=ZONES.CLOSE && value < 0.21f))
            {
                return ZONES.FAR;
            }
            else if ((lastZone == ZONES.CLOSEST && value < 0.27f) || 
                ( lastZone != ZONES.CLOSEST && value < 0.35f))
            {
                return ZONES.CLOSE;
            }
            return ZONES.CLOSEST;
        }

        public void addRangerListener(RangerListener listener)
        {
            rangerListeners.Add(listener);
        }

        public void fireZoneEvent()
        {
            RangerEvent e = new RangerEvent(currentZone, currentLane, rangerData);
            foreach (RangerListener listener in rangerListeners)
            {
                // HACK temporary remove... (not)
                listener.enteredZone(e);
            }
        }

        public void fireDistanceEvent()
        {
            RangerEvent e = new RangerEvent(currentZone, currentLane, rangerData);
            foreach (RangerListener listener in rangerListeners)
            {
                listener.distanceEvent(e);
            }
        }
    }

    public class RangerEvent
    {
        private ZONES currentZone = ZONES.FARTHEST;
        private int currentLane =-1;

        private float[] rawValues = { 0, 0, 0, 0 }; 
        private string[] Zones_Names = { "CLOSEST", "CLOSE", "FAR", "FARTHEST" };

        public RangerEvent(ZONES curZone, int curLane, float[] rData)
        {
            currentZone = curZone;
            currentLane = curLane;
            Array.Copy(rData, rawValues, 4);
        }
        public RangerEvent()
        {
        }
        public ZONES getCurrentZone()
        {
            return currentZone;
        }

        public int getCurrentLane()
        {
            return currentLane;
        }


        public string getZoneNames(ZONES zone)
        {
            return Zones_Names[(int)zone];
        }
        public float[] getRawValues()
        {
            return rawValues;
        }

        public float getMeanValue ()
        {
            return (rawValues[0] + rawValues[1] + rawValues[2] + rawValues[3]) / 4.0f;
        }
    }

    public interface RangerListener
    {
        void enteredZone(RangerEvent e);
        void distanceEvent(RangerEvent e);
    }
}
